#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "Terminal.hpp"

using namespace std;

//多线程，群聊版本（可以一边看消息，一边输出消息）
void Usage(std::string proc) {
    std::cout << "\n\tUsage: " << proc << " serverip serverport\n" << std::endl;
}

struct ThreadData {
    struct sockaddr_in server;
    int sockfd;
    std::string serverip;//用于获取谁上线
};

void* recv_message(void* args) {
    // OpenTerminal(); // ./udpclient 159.75.96.122 8888 2>/dev/pts/1 该方式也是同样效果
    ThreadData* td = static_cast<ThreadData*>(args);
    char buffer[1024];
    while (true) {
        memset(buffer, 0, sizeof(buffer));
        struct sockaddr_in tmp;
        socklen_t len = sizeof(tmp);
        ssize_t s = recvfrom(td->sockfd, buffer, 1023, 0, (struct sockaddr*)&tmp, &len);
        if (s > 0) {
            buffer[s] = 0;
            cerr << buffer << endl;
        }
    }
}

void* send_message(void* args) {
    ThreadData* td = static_cast<ThreadData*>(args);
    string message;
    socklen_t len = sizeof(td->server);

    std::string welcome = td->serverip;
    welcome += "comming...";
    //没有登录功能，通过发消息方式获取谁上线（即一上线就能获取，而不是第一次发消息）
    sendto(td->sockfd, welcome.c_str(), welcome.size(), 0, (struct sockaddr*)&(td->server), len);

    while (true) {
        cout << "Please Enter@ ";
        getline(cin, message);
        sendto(td->sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&(td->server), len);
    }
}

// ./udpclient serverip serverport
int main(int argc, char* argv[]) {
    if (argc != 3) {
        Usage(argv[0]);
        exit(0);
    }
    string serverip = argv[1];
    uint16_t serverport = stoi(argv[2]);

    //构建服务器套接字
    struct ThreadData td;
    bzero(&td.server, sizeof(td.server));
    td.server.sin_family = AF_INET;
    td.server.sin_port = htons(serverport);//要保证端口号是网络字节序列，因为要发给对方。htons主机序列转网络序列
    td.server.sin_addr.s_addr = inet_addr(serverip.c_str());
    td.serverip = serverip;//用于获取谁上线
    
    td.sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (td.sockfd < 0) {
        cout << "socker error" << endl;
        return 1;
    }
      
    pthread_t recvr, sender;
    pthread_create(&recvr, nullptr, recv_message, &td);
    pthread_create(&sender, nullptr, send_message, &td);

    pthread_join(recvr, nullptr);
    pthread_join(sender, nullptr);

    close(td.sockfd);
    return 0;
}



// // 单线程，非群聊版本
// void Usage(std::string proc) {
//     std::cout << "\n\tUsage: " << proc << " serverip serverport\n" << std::endl;
// }

// // ./udpclient serverip serverport
// int main(int argc, char* argv[]) {
//     if (argc != 3) {
//         Usage(argv[0]);
//         exit(0);
//     }
//     string serverip = argv[1];
//     uint16_t serverport = stoi(argv[2]);

//     //构建服务器套接字
//     struct sockaddr_in server;
//     bzero(&server, sizeof(server));
//     server.sin_family = AF_INET;
//     server.sin_port = htons(serverport);//要保证端口号是网络字节序列，因为要发给对方。htons主机序列转网络序列
//     server.sin_addr.s_addr = inet_addr(serverip.c_str());
//     socklen_t len = sizeof(server);

//     int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
//     if (sockfd < 0) {
//         cout << "socker error" << endl;
//         return 1;
//     }
//     // 客户端一定要bind，因为需要自己的IP和端口。只不过不需要用户显示的bind，一般由OS自由随机选择
//     // 一个端口号只能被一个进程bind，sever和client都是如此
//     // 如果允许客户端显示绑定端口号，那么各服务器厂商还得协调，否则会冲突
//     // client的port是多少不重要，只要能保证主机上的唯一性就可以
//     //系统什么时候给客户端bind？首次发送数据的时候
//     string message;
//     char buffer[1024];
//     while (true) {
//         //如果是群聊，那么只要不输入消息就会在getline阻塞导致收不到消息
//         cout << "Please Enter@ ";
//         getline(cin, message);
//         sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, len);

//         struct sockaddr_in tmp;
//         socklen_t len = sizeof(tmp);
//         ssize_t s = recvfrom(sockfd, buffer, 1023, 0, (struct sockaddr*)&tmp, &len);
//         if (s > 0) {
//             buffer[s] = 0;
//             cout << buffer << endl;
//         }
//     }

//     close(sockfd);
//     return 0;
// }